﻿using System;
using System.Collections.Generic;
using System.Linq;
using Go.Tools;

namespace Go.Model
{
    public class Group
    {
        private List<Position> _positions;
        private Player _player;
        private List<Position> _neighborPositions;
        private List<Position> _libertyPositions;
        private List<Position> listaDePosicionesDeOjo;

        public Group(Position aPosition, Player player)
        {
            _positions = new List<Position>();
            _player = player;
            _libertyPositions = new List<Position>();
            aPosition.addStoneToGroup(this);
            _neighborPositions = new List<Position>(aPosition.NeighborPositions);
            this.associateGroups();
            updateGroup();
            player.addGroup(this);
        }

        //Devuelve true si el grupo pasado por parametro es del mismo color que este
        public bool isSameColor(Group aGroup )
        {
            return Color == aGroup.Color;
        }

        // Calcula las libertades del grupo seteandola en _libertyPositions
        public void calculateLiberty() {
            _libertyPositions.Clear();
            foreach (Position aPosition in _neighborPositions)
            {
                if (aPosition.isEmpty() )
                _libertyPositions.Add (aPosition);
            }
            _libertyPositions.Distinct().ToList();
        }

        //Une grupos de fichas
        private void associateGroups() 
        {
            foreach (Position aPosition in _neighborPositions)
            {
                if (!aPosition.isEmpty() && this.itMustBeAssociated(aPosition.Group))
                {
                    this.joinGroup(aPosition.Group);
                }
            }
        }

        public void joinGroup(Group aGroupToJoin)
        {
            foreach (Position aPosition in aGroupToJoin.Positions)
            {
                this.addPosition(aPosition);
                aPosition.Group = this;
            }
            _player.removeGroup(aGroupToJoin);
        }

        public void disassociateGroups(Position pos)
        {
            if (_positions.Count > 1)
            {
                removePositionOfGroup(pos);
                foreach (Position aPos in _positions)
                {
                    aPos.Group = null;
                    Group aGroup = new Group(aPos, _player);
                }
            }
            else {
                removePositionOfGroup(pos);
            }
        }

        private void removePositionOfGroup(Position position) {
            _player.removeGroup(position.Group);
            this._positions.Remove(position);
            position.Group = null;
        }

        // Devuelve true si el Grupo que se pasa por parametro es del = color que la instancia en cuestion
        // y ademas no son el mismo
        private bool itMustBeAssociated(Group aGroup) {
            return (this.Color == aGroup.Color) && (aGroup != this);
        }

        // Calcula las posiciones vecinas del grupo seteandola en la variable neighborPositions
        public void calculateNeighborsPositions()
        {
            _neighborPositions.Clear();
            foreach (Position aPosition in _positions)
            {
                foreach (Position aPositionCanBeNeighbor in aPosition.NeighborPositions)
                {
                    if (aPositionCanBeNeighbor.Group != this)
                        _neighborPositions.Add(aPositionCanBeNeighbor);
                }
            }
            _neighborPositions= _neighborPositions.Distinct().ToList();
        }

        //Actualiza el grupo tanto sus libertades como las posiciones vecinas que tiene
        public void updateGroup() {
            calculateNeighborsPositions();
            calculateLiberty();
            obtenerPosicionesDeOjos();
        }

        //Devuelve true si no tiene libertades
        public bool isDead()
        {
            return _libertyPositions.Count == 0;
        }
                   
        public void addPosition (Position aPosition)
        {
            if (!_positions.Exists(item => (item == aPosition)) )
                _positions.Add(aPosition);
        }

        //Devuelve true si la posicion pasada por parametro esta en el grupo
        public bool hasPosition(Position aPosition)
        {
            return this._positions.Find(item => item == aPosition) != null;
        }

        public List<Position> Positions
        {
            get { return _positions; }
        }

        public List<Position> NeighborPositions
        {
            get { return _neighborPositions; }
        }

        public Color Color
        {
            get { return _player.Color; }
        }

        public List<Position> LibertyPositions
        {
            get { return _libertyPositions; }
            set { _libertyPositions = value; }
        }

        private void obtenerPosicionesDeOjos()
        {
            this.listaDePosicionesDeOjo = new List<Position>();
            
            foreach (Position aPosL in _libertyPositions)
            {
                if ((this.existePosicionArriba(aPosL.Coordinate)) && (this.existePosicionAbajo(aPosL.Coordinate)) &&
                    (this.existePosicionDerecha(aPosL.Coordinate)) && (this.existePosicionIzquierda(aPosL.Coordinate)))
                {
                    this.listaDePosicionesDeOjo.Add(aPosL);
                }
            }
        }

        public bool isAliveGroup()
        {
            if (this.listaDePosicionesDeOjo.Count > 0) 
            {
                Position p = this.listaDePosicionesDeOjo.First();
                this.listaDePosicionesDeOjo.Remove(p);
                searchConsecutiveEyes(p);
               
                return this.listaDePosicionesDeOjo.Count > 0;
            }
            else
            {
                return false;
            }
        }

        private void searchConsecutiveEyes(Position p)
        {
            List<Position> vecinos = p.NeighborPositions;
            foreach (Position posAux in vecinos)
            { 
                if(listaDePosicionesDeOjo.Contains(posAux))
                {
                    listaDePosicionesDeOjo.Remove(posAux);
                    searchConsecutiveEyes(posAux);
                }
            }
        }

        private bool existePosicionArriba(Coordinate coord)
        {
            foreach (Position apos in _positions)
            {
                if (coord.y < apos.Coordinate.y && coord.x == apos.Coordinate.x)
                    return true;
            }
            return false;
        }

        private bool existePosicionAbajo(Coordinate coord)
        {
            foreach (Position apos in _positions)
            {
                if (coord.y > apos.Coordinate.y && coord.x == apos.Coordinate.x)
                    return true;
            }
            return false;
        }

        private bool existePosicionDerecha(Coordinate coord)
        {
            foreach (Position apos in _positions)
            {
                if (coord.y == apos.Coordinate.y && coord.x < apos.Coordinate.x)
                    return true;
            }
            return false;
        }

        private bool existePosicionIzquierda(Coordinate coord)
        {
            foreach (Position apos in _positions)
            {
                if (coord.y == apos.Coordinate.y && coord.x > apos.Coordinate.x)
                    return true;
            }
            return false;
        }

        // k jugadas!
        public List<Position> kJugadas(int k)
        {
            List<Position> resultado = new List<Position>();
            List<Position> listAux = new List<Position>();

            List<Position> libres = new List<Position>();

            foreach (Position posLibre in LibertyPositions)
            {
                resultado.Add(posLibre);
            }
            
            List<Position> lista = new List<Position>(resultado);

            for(int i = 1; i < k; i++){
                foreach (Position l in lista)
                {
                    libres.Clear();
                    foreach (Position aPos in l.NeighborPositions)
                    {
                       if (aPos.isEmpty() )
                            libres.Add (aPos);
                    }
                    foreach (Position posLibre in libres)
                    {
                        resultado.Add(posLibre);
                        listAux.Add(posLibre);
                    }
                }
                lista.Clear();
                lista = new List<Position>(listAux).Distinct().ToList();
            }
            return resultado.Distinct().ToList();
        }

    }
}